package com.webwork.common.util;

import java.io.BufferedInputStream;
import java.io.BufferedOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.InputStream;
import java.io.OutputStream;
import java.nio.charset.Charset;
import java.util.Enumeration;
import java.util.zip.ZipEntry;
import java.util.zip.ZipException;
import java.util.zip.ZipFile;
import java.util.zip.ZipInputStream;
import java.util.zip.ZipOutputStream;

import javax.servlet.ServletException;

import org.apache.commons.io.FileUtils;
import org.apache.commons.lang3.StringUtils;

/**
 * 压缩工具类
 *
 */
public class ZipUtil {
	
	private static final int BUFFER_SIZE = 2 * 1024;
	
	/**
	 * 压缩文件
	 * @param file//要压缩的文件
	 * @param zipFile 压缩后的文件
	 * @param keepDir
	 *            是否保留原来的目录结构,true:保留目录结构;
	 *            false:所有文件跑到压缩包根目录下(注意：不保留目录结构可能会出现同名文件,会压缩失败)
	 * @throws Exception
	 */
	public static void zip(File file, File zipFile, boolean keepDir) throws Exception {
		ZipOutputStream out = new ZipOutputStream(new FileOutputStream(zipFile));
		if (keepDir)
			zip(file, out, file.getName());
		else 
			zip(out, file);
		System.out.println("zip done");
		out.close();
	}
	
	private static void zip(ZipOutputStream out, File f) throws Exception {
		if (f.isDirectory()) {
			File[] fl = f.listFiles();
			for (int i = 0; i < fl.length; i++) {
				zip(out, fl[i]);
			}
		} else {
			out.putNextEntry(new ZipEntry(f.getName()));	//开始写入新的ZIP文件条目并将流定位到条目数据的开始处
			FileInputStream in = new FileInputStream(f);
			BufferedInputStream bis = new BufferedInputStream(in);
			int b;
			while ((b = bis.read()) != -1) {
				out.write(b);
			}
			bis.close();
			in.close();
		}
	}
	
	/**
	 * 递归压缩方法(保留目录结构)
	 *
	 * @param sourceFile 源文件
	 * @param zos zip输出流
	 * @param name 压缩后的名称
	 * 
	 * @throws Exception
	 */
	private static void zip(File sourceFile, ZipOutputStream zos, String name) throws Exception {
		byte[] buf = new byte[BUFFER_SIZE];
		if (sourceFile.isFile()) {
			// 向zip输出流中添加一个zip实体，构造器中name为zip实体的文件的名字
			zos.putNextEntry(new ZipEntry(name));
			// copy文件到zip输出流中
			int len;
			FileInputStream in = new FileInputStream(sourceFile);
			while ((len = in.read(buf)) != -1) {
				zos.write(buf, 0, len);
			}
			// Complete the entry
			zos.closeEntry();
			in.close();
		} else {
			File[] listFiles = sourceFile.listFiles();
			if (listFiles == null || listFiles.length == 0) {
				// 需要保留原来的文件结构时,需要对空文件夹进行处理
				// 空文件夹的处理
				zos.putNextEntry(new ZipEntry(name + "/"));
				// 没有文件，不需要文件的copy
				zos.closeEntry();
			} else {
				for (File file : listFiles) {
					// 判断是否需要保留原来的文件结构
					// 注意：file.getName()前面需要带上父文件夹的名字加一斜杠,
					// 不然最后压缩包中就不能保留原来的文件结构,即：所有文件都跑到压缩包根目录下了
					zip(file, zos, name + "/" + file.getName());
				}
			}
		}
	}

	public static void unZip(File zipFile, String unzipFilePath, boolean includeZipFileName) {
		// 如果解压后的文件保存路径包含压缩文件的文件名，则追加该文件名到解压路径
		if (includeZipFileName) {
			String fileName = zipFile.getName();
			if (!StringUtils.isEmpty(fileName)) {
				fileName = fileName.substring(0, fileName.lastIndexOf("."));
			}
			unzipFilePath = unzipFilePath + File.separator + fileName;
		}
		// 创建解压缩文件保存的路径
		File unzipFileDir = new File(unzipFilePath);
		if (!unzipFileDir.exists() || !unzipFileDir.isDirectory()) {
			unzipFileDir.mkdirs();
		}
		// 开始解压
		String entryFilePath = null, entryDirPath = null;
		File entryFile = null, entryDir = null;
		int index = 0, count = 0, bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		FileInputStream in = null;
		ZipInputStream zip = null;
		try {
			in = new FileInputStream(zipFile);
			zip = new ZipInputStream(in);
			ZipEntry entry = null;
			while((entry = zip.getNextEntry()) != null) {
				// 构建压缩包中一个文件解压后保存的文件全路径
				entryFilePath = unzipFilePath + File.separator + entry.getName();
				// 构建解压后保存的文件夹路径
				entryFilePath = entryFilePath.replace("/", File.separator);
				index = entryFilePath.lastIndexOf(File.separator);
				if (index != -1) {
					entryDirPath = entryFilePath.substring(0, index);
				} else {
					entryDirPath = "";
				}
				entryDir = new File(entryDirPath);
				// 如果文件夹路径不存在，则创建文件夹
				if (!entryDir.exists() || !entryDir.isDirectory()) {
					entryDir.mkdirs();
				} else {
					// 创建解压文件
					entryFile = new File(entryFilePath);
					// 写入文件
					bos = new BufferedOutputStream(new FileOutputStream(entryFile));
					bis = new BufferedInputStream(zip);
					while ((count = bis.read(buffer, 0, bufferSize)) != -1) {
						bos.write(buffer, 0, count);
					}
					bos.flush();
				}
			}
		} catch (IOException e) {
			e.printStackTrace();
		} finally {
			try {
				bos.close();
				bis.close();
				zip.close();
				in.close();
			} catch (IOException e) {
				e.printStackTrace();
			}
		}
	}
	
	public static void unZip(File zipFile, String unzipFilePath) throws ZipException, IOException {
		// 创建解压缩文件保存的路径
		File unzipFileDir = new File(unzipFilePath);
		if (!unzipFileDir.exists() || !unzipFileDir.isDirectory()) {
			unzipFileDir.mkdirs();
		}
		// 开始解压
		String entryFilePath = null;
		File entryFile = null, entryDir = null;
		int count = 0, bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		FileInputStream in = new FileInputStream(zipFile);
		ZipInputStream zip = new ZipInputStream(in);
		ZipEntry entry = null;
		while((entry = zip.getNextEntry()) != null) {
			// 构建压缩包中一个文件解压后保存的文件全路径
			entryFilePath = unzipFilePath + File.separator + entry.getName();
			// 构建解压后保存的文件夹路径
			entryFilePath = entryFilePath.replace("/", File.separator);
			if (entry.isDirectory()) {
				entryDir = new File(entryFilePath);
				if(!entryDir.exists()) {	// 如果文件夹路径不存在，则创建文件夹
					entryDir.mkdirs();
				}
			} else {
				// 创建解压文件
				entryFile = new File(entryFilePath);
				// 写入文件
				bos = new BufferedOutputStream(new FileOutputStream(entryFile));
				bis = new BufferedInputStream(zip);
				while ((count = bis.read(buffer, 0, bufferSize)) != -1) {
					bos.write(buffer, 0, count);
				}
				bos.flush();
				bos.close();
			}
		}
		zip.close();
	}
	
	public static void unzip(String zipFilePath, String unzipFilePath) throws ZipException, IOException {
		// 创建解压缩文件保存的路径
		File unzipFileDir = new File(unzipFilePath);
		if (!unzipFileDir.exists() || !unzipFileDir.isDirectory()) {
			unzipFileDir.mkdirs();
		}
		// 开始解压
		ZipEntry entry = null;
		String entryFilePath = null;
		File entryFile = null, entryDir = null;
		int count = 0, bufferSize = 1024;
		byte[] buffer = new byte[bufferSize];
		BufferedInputStream bis = null;
		BufferedOutputStream bos = null;
		ZipFile zip = new ZipFile(new File(zipFilePath));
		Enumeration<ZipEntry> entries = (Enumeration<ZipEntry>) zip.entries();
		// 循环对压缩包里的每一个文件进行解压
		while (entries.hasMoreElements()) {
			entry = entries.nextElement();
			// 构建压缩包中一个文件解压后保存的文件全路径
			entryFilePath = unzipFilePath + File.separator + entry.getName();
			// 构建解压后保存的文件夹路径
			entryFilePath = entryFilePath.replace("/", "\\");
			if(entry.isDirectory()) {
				entryDir = new File(entryFilePath);
				if(!entryDir.exists()) {	// 如果文件夹路径不存在，则创建文件夹
					entryDir.mkdirs();
				}
			} else {
				// 创建解压文件
				entryFile = new File(entryFilePath);
				// 写入文件
				bos = new BufferedOutputStream(new FileOutputStream(entryFile));
				bis = new BufferedInputStream(zip.getInputStream(entry));
				while ((count = bis.read(buffer, 0, bufferSize)) != -1) {
					bos.write(buffer, 0, count);
				}
				bos.flush();
				bos.close();
			}
		}
		zip.close();
	}
	
    /**
     * zip解压
     * @param zipFile        zip源文件
     * @param descDir     解压后的目标文件夹
     * @throws IOException 解压失败会抛出运行时异常
     */
    @SuppressWarnings({ "rawtypes", "resource" })
	public static void unFileZip(File zipFile, String descDir) throws IOException {
        File pathFile = new File(descDir);
		if (!pathFile.exists()) {
			pathFile.mkdirs();
		}
		//解决zip文件中有中文目录或者中文文件
        ZipFile zip = new ZipFile(zipFile, Charset.forName("GBK"));
		for (Enumeration entries = zip.entries(); entries.hasMoreElements();) {
			ZipEntry entry = (ZipEntry) entries.nextElement();
			String zipEntryName = entry.getName();
			InputStream in = zip.getInputStream(entry);
			String outPath = (descDir + File.separator + zipEntryName).replaceAll("\\*", "/");

			//判断路径是否存在,不存在则创建文件路径
			if (outPath.lastIndexOf('/') > -1) {
				File file = new File(outPath.substring(0, outPath.lastIndexOf('/')));
				if (!file.exists()) {
					file.mkdirs();
				}
			}
			//判断文件全路径是否为文件夹,如果是上面已经上传,不需要解压
			if (new File(outPath).isDirectory()) {
				continue;
			}
			//输出文件路径信息
			System.out.println(outPath);
			OutputStream out = new FileOutputStream(outPath);
			byte[] buf1 = new byte[1024];
			int len;
			while ((len = in.read(buf1)) > 0) {
				out.write(buf1, 0, len);
			}
			in.close();
			out.close();
		}
		System.out.println("******************解压完毕********************");
    }
	
	
	/**
	  * 将文件/目录进行压缩(可以解决压缩包中的文件夹和文件没有图标问题)
	  * @param sourceFile 原文件/目录
	  * @param targetZipFile 压缩后目标文件
	  * @throws IOException 
	  */
	public static void zipFiles(File sourceFile, File targetZipFile) throws IOException {
		ZipOutputStream outputStream = null;
		try {
			outputStream = new ZipOutputStream(new FileOutputStream(targetZipFile));
			addEntry("", sourceFile, outputStream);
		} catch (Exception e) {
			throw new IOException(e);
		} finally {
			outputStream.close();
		}
	}
	
	/**
	 * 将文件写入到zip文件中
	 * @param source
	 * @param outputstream
	 * @throws IOException
	 * @throws ServletException
	 */
	private static void addEntry(String base, File source, ZipOutputStream outputstream)
			throws IOException, ServletException {
		FileInputStream is = null;
		try {
			String entry = base + source.getName();
			if (source.isDirectory()) {
				for (File file : source.listFiles()) {
					// 递归导入文件
					addEntry(entry + File.separator, file, outputstream);
				}
			} else {
				is = FileUtils.openInputStream(source);
				if (is != null) {
					outputstream.putNextEntry(new ZipEntry(entry));
					int len = 0;
					byte[] buffer = new byte[10 * 1024];
					while ((len = is.read(buffer)) > 0) {
						outputstream.write(buffer, 0, len);
						outputstream.flush();
					}
					outputstream.closeEntry();
				}
			}
		} catch (IOException e) {
			throw e;
		} finally {
			if (is != null) {
				is.close();
			}
		}
	}
	
}
